package com.sushengren.easyword.anchor;

import com.sushengren.easyword.model.AnchorContext;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBookmark;
import org.w3c.dom.Node;

import java.util.ArrayList;
import java.util.List;

/**
 * 书签定位
 *
 * @author sushengren
 */
public class BookmarkAnchor implements Anchor {

    public static final String BOOKMARK_START_FLAG = "w:bookmarkStart";
    public static final String BOOKMARK_END_FLAG = "w:bookmarkEnd";

    @Override
    public List<AnchorContext> locate(XWPFDocument document) {
        List<AnchorContext> result = new ArrayList<>();
        for (XWPFTable table : document.getTables()) {
            for (XWPFTableRow row : table.getRows()) {
                for (XWPFTableCell tableCell : row.getTableCells()) {
                    for (XWPFParagraph paragraph : tableCell.getParagraphs()) {
                        if (!CollectionUtils.isEmpty(paragraph.getCTP().getBookmarkStartList())) {
                            result.addAll(getAnchorContext(paragraph));
                        }
                    }
                }
            }
        }
        for (XWPFParagraph paragraph : document.getParagraphs()) {
            if (!CollectionUtils.isEmpty(paragraph.getCTP().getBookmarkStartList())) {
                result.addAll(getAnchorContext(paragraph));
            }
        }
        return buildTree(result);
    }

    /**
     * 获取定位上下文
     *
     * @param paragraph 段落
     * @return 定位上下文
     */
    private List<AnchorContext> getAnchorContext(XWPFParagraph paragraph) {
        List<AnchorContext> result = new ArrayList<>();
        for (CTBookmark bookmark : paragraph.getCTP().getBookmarkStartList()) {
            AnchorContext anchorContext = new AnchorContext();
            anchorContext.setKey(bookmark.getName());
            anchorContext.setDocument(paragraph.getDocument());
            anchorContext.setParagraph(paragraph);
            List<Node> nodeList = findBookmarkEnd(bookmark.getDomNode());
            anchorContext.setNodeList(nodeList);
            List<XWPFRun> runs = new ArrayList<>();
            for (XWPFRun run : paragraph.getRuns()) {
                if (nodeList.contains(run.getCTR().getDomNode())) {
                    runs.add(run);
                }
            }
            anchorContext.setRuns(runs);
            anchorContext.setAnchor(this);
            result.add(anchorContext);
        }
        return result;
    }

    /**
     * 查找结束标志标签
     *
     * @param node 节点
     * @return 节点列表
     */
    private List<Node> findBookmarkEnd(Node node) {
        List<Node> result = new ArrayList<>();
        result.add(node);
        Node nextSibling = node.getNextSibling();
        boolean bookmarkEnd = false;
        while (nextSibling != null) {
            result.add(nextSibling);
            if (BOOKMARK_END_FLAG.equals(nextSibling.getNodeName())) {
                bookmarkEnd = true;
                break;
            }
            nextSibling = nextSibling.getNextSibling();
        }
        return bookmarkEnd ? result : findBookmarkEnd(node.getParentNode());
    }

    /**
     * 构建树结构，根据Key及分隔符
     *
     * @param anchorContextList 锚上下文列表
     * @return 树结构节点列表
     */
    private List<AnchorContext> buildTree(List<AnchorContext> anchorContextList) {
        List<AnchorContext> result = new ArrayList<>();
        if (anchorContextList == null || anchorContextList.isEmpty()) {
            return result;

        }
        for (AnchorContext anchorContext : anchorContextList) {
            String key = anchorContext.getKey();
            if (key.split(PlaceholderAnchor.FLAG_SPLIT).length == 1) {
                result.add(anchorContext);
            }
            for (AnchorContext child : anchorContextList) {
                String childKey = child.getKey();
                int lastIndex = childKey.lastIndexOf(PlaceholderAnchor.FLAG_SPLIT);
                if (lastIndex != -1 && key.equals(childKey.substring(0, lastIndex))) {
                    anchorContext.setChildren(child);
                }
            }
        }
        return result;
    }

}
